#include "stdafx.h"
#include "GraphicalUserInterface.h"
#include "RenderSystem.h"
#include "MessageSystem.h"

namespace Gui
{
	Widget::Widget(EWidgetType type) : m_type(type)
	{
		m_text_position = Vector2(0.0f, 0.0f);
	}

	EWidgetType Widget::GetType() const { return m_type; };
	float Widget::GetX() const { return m_x; };
	float Widget::GetY() const { return m_y; };
	float Widget::GetWidth() const { return m_width; };
	float Widget::GetHeight() const { return m_height; };

	float Widget::GetTX() const { return m_tx; };
	float Widget::GetTY() const { return m_ty; };
	float Widget::GetTW() const { return m_tw; };
	float Widget::GetTH() const { return m_th; };

	void Widget::SetTX(float tx) { m_dirty = true; m_tx = tx; };
	void Widget::SetTY(float ty) { m_dirty = true; m_ty = ty; };
	void Widget::SetTW(float tw) { m_dirty = true; m_tw = tw; };
	void Widget::SetTH(float th) { m_dirty = true; m_th = th; };
	void Widget::SetTexcoords(float tx, float ty, float tw, float th) { m_dirty = true; m_tx = tx; m_ty = ty; m_tw = tw; m_th = th; };

	void Widget::SetX(float x) { m_dirty = true; m_x = x; };
	void Widget::SetY(float y) { m_dirty = true; m_y = y; };
	void Widget::SetWidth(float width) { m_dirty = true; m_width = width; };
	void Widget::SetHeight(float height) { m_dirty = true; m_height = height; };
	void Widget::SetVisible(bool visible) { m_dirty = true; m_visible = visible; };

	bool Widget::IsVisible() const { return m_visible; };
	bool Widget::IsEnabled() const { return m_enabled; };
	bool Widget::IsDirty() const { return m_dirty; };
	void Widget::Clean() { m_dirty = false; };

	bool Widget::IsInside(int x, int y)	{ return false; };

	void Widget::SetTextPosition(Vector2 pos) { m_dirty = true; m_text_position = pos; };
	Vector2 Widget::GetTextPosition() const { return m_text_position; };

	//**************************************************
	Button::Button() : Widget(WT_BUTTON)
	{
		m_color = 0xffffffff;
	};

	bool Button::IsInside(int x, int y)
	{
		if (x >= m_x && 
			x <= m_x + m_width &&
			y >= m_y &&
			y <= m_y + m_height)
			return true;

		return false;
	};

	unsigned int Button::GetColor() const { return m_color; };
	void Button::SetColor(unsigned int color) { m_dirty = true; m_color = color; };
	String Button::GetText() const { return m_text; };
	void Button::SetText(const String& text) { m_dirty = true; m_text = text; };
	void Button::SetFontId(const FontId& id) { m_dirty = true; m_font_id = id; };
	void Button::SetFontSize(unsigned int font_size) { m_dirty = true; m_font_size = font_size; };
	unsigned int Button::GetFontSize() const { return m_font_size; };
	FontId Button::GetFontId() const { return m_font_id; };

	void Button::SetButtonType(EButtonType type) { m_button_type = type; };
	EButtonType Button::GetButtonType() { return m_button_type; };

	// Textfield
	//******************************************
	Textfield::Textfield() : Widget(WT_TEXTFIELD)
	{
		m_color = 0xffffffff;
	}

	unsigned int Textfield::GetColor() const { return m_color; };
	void Textfield::SetColor(unsigned int color) { m_dirty = true; m_color = color; };
	String Textfield::GetText() const { return m_text; };
	void Textfield::SetText(const String& text) { m_dirty = true; m_text = text; };
	void Textfield::SetFontId(const FontId& id) { m_dirty = true; m_font_id = id; };
	void Textfield::SetFontSize(unsigned int font_size) { m_dirty = true; m_font_size = font_size; };
	unsigned int Textfield::GetFontSize() const { return m_font_size; };
	FontId Textfield::GetFontId() const { return m_font_id; };

}; // namespace Gui


#include "ResourceManager.h"
GuiManager::GuiManager(RenderSystem* render_system, MessageSystem* message_system, ResourceManager* resource_manager)
{
	m_render_system = render_system;
	m_message_system = message_system;

	m_button_icons = resource_manager->GetTexture("gui_icons.tga");
}

GuiManager::~GuiManager()
{
}

bool GuiManager::Init(int width, int height, unsigned int max_vertices)
{
	m_message_system->Register(EVT_MOUSEMOVE, this, &GuiManager::OnMouseMove);
	m_message_system->Register(EVT_MOUSEBUTTON, this, &GuiManager::OnMouseButton);

	//font builder
	m_font_builder.Init();
	m_font_builder.LoadFontFace("../data/font/mecha.ttf", 0);

	m_width = width;
	m_height = height;

	// Create shader
	m_render_system->CreateShader(&m_gui.m_shader, "../data/shader/gui_vs.hlsl", "../data/shader/gui_ps.hlsl");
	
	int vss[]={sizeof(OncePerFrame)};

	m_gui.m_shader.AllocateVSConstants(1, vss);
	OncePerFrame frame;
	frame.projection = DirectX::XMMatrixOrthographicLH((float)width, (float)height, 0.0f, 1.0f);
	frame.view = DirectX::XMMatrixIdentity();

	m_gui.m_shader.SetVSConstant(0, sizeof(OncePerFrame), &frame);
	m_render_system->CreateConstantBuffers(&m_gui.m_shader);

	Sampler sampler_gui;
	m_render_system->CreateSamplerState(&sampler_gui,AM_WRAP,AM_WRAP,AM_WRAP,FM_LINEAR,CM_NEVER);
	m_gui.m_shader.AllocateSamplers(1);
	m_gui.m_shader.SetSampler(0,sampler_gui);
	
	D3D10_INPUT_ELEMENT_DESC desc[]=
	{
		{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0},
		{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0},
		{"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 20, D3D10_INPUT_PER_VERTEX_DATA, 0}
	};

	m_render_system->CreateInputLayout(&m_gui.m_shader, desc, sizeof(desc)/sizeof(desc[0]));

	m_gui.m_max_vertices = max_vertices * 4;
	unsigned short* indices = new unsigned short[max_vertices * 6];
	
	for (unsigned int i = 0, index = 0; i < max_vertices * 6; i+=6)
	{
		indices[i+0] = index+0;
		indices[i+1] = index+1;
		indices[i+2] = index+2;

		indices[i+3] = index+2;
		indices[i+4] = index+3;
		indices[i+5] = index+0;

		index += 4;
	}

	m_gui.m_renderable.m_count = 0;
	m_render_system->CreateIndexBuffer(&m_gui.m_renderable.m_index_buffer, max_vertices * 6, sizeof(unsigned short), indices);
	
	m_gui.m_vertices = new Gui::Vertex[m_gui.m_max_vertices];
	m_render_system->CreateVertexBuffer(&m_gui.m_renderable.m_vertex_buffer, BM_DYNAMIC, m_gui.m_max_vertices, sizeof(Gui::Vertex), m_gui.m_vertices);



	// create text resources
	m_render_system->CreateShader(&m_font.m_shader, "../data/shader/text_vs.hlsl", "../data/shader/text_ps.hlsl");
	
	m_font.m_shader.AllocateVSConstants(1, vss);
	
	m_font.m_shader.SetVSConstant(0, sizeof(OncePerFrame), &frame);
	m_render_system->CreateConstantBuffers(&m_font.m_shader);
	
	D3D10_INPUT_ELEMENT_DESC tdesc[]=
	{
		{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0},
		{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0},
		{"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 20, D3D10_INPUT_PER_VERTEX_DATA, 0}
	};

	m_render_system->CreateInputLayout(&m_font.m_shader, tdesc, sizeof(tdesc)/sizeof(tdesc[0]));

	m_font.m_renderable.m_count = 0;
	m_render_system->CreateIndexBuffer(&m_font.m_renderable.m_index_buffer,
		max_vertices * 6,
		sizeof(unsigned short),
		indices);

	delete [] indices;

	m_font.m_max_vertices = max_vertices * 4;
	m_font.m_vertices = new Text::Vertex[m_font.m_max_vertices];
	m_render_system->CreateVertexBuffer(
		&m_font.m_renderable.m_vertex_buffer,
		BM_DYNAMIC,
		m_font.m_max_vertices,
		sizeof(Text::Vertex),
		m_font.m_vertices);

	// font texture
	m_render_system->CreateTexture(&m_font.m_texture,
		BM_DEFAULT, FMT_R8G8B8A8, m_font_builder.GetImage()->GetWidth(), m_font_builder.GetImage()->GetHeight());
	
	Sampler sampler;
	m_render_system->CreateSamplerState(&sampler, AM_CLAMP, AM_CLAMP, AM_CLAMP, FM_LINEAR, CM_NEVER);
	m_font.m_shader.AllocateSamplers(1);
	m_font.m_shader.SetSampler(0, sampler);

	m_render_system->CreateRasterizerState(&m_rasterizer,CULL_NONE,FILL_SOLID);
	m_render_system->CreateDepthState(&m_depth);


	return true;
}
void GuiManager::Shut()
{
	delete [] m_gui.m_vertices;
	m_gui.m_renderable.Release();
	m_gui.m_shader.Release();

	
	m_message_system->Unregister(EVT_MOUSEMOVE, this);
	m_message_system->Unregister(EVT_MOUSEBUTTON, this);

	//for (auto it = m_widgets.begin(); it != m_widgets.end(); it++)
	//{
	//	delete *it;
	//}

	m_font_builder.Shut();

	m_font.m_renderable.Release();
	m_font.m_shader.Release();
	m_font.m_texture.Release();

	delete [] m_font.m_vertices;
	
	m_button_icons->Release();
}

void GuiManager::Update()
{
	unsigned int k = 0;
	
	for (unsigned int i = 0; i < m_widgets.size(); i++)
	{
		if (m_widgets[i]->IsDirty())
		{
			k = m_widgets.size() + 1;
			break;
		}
		k++;
	}

	if (k<=m_widgets.size())
		return;

	m_font.m_renderable.m_count = 0;

	unsigned int ic = 0;

	

	for (unsigned int i = 0; i < m_widgets.size(); i++)
	{
		m_widgets[i]->Clean();

		if (m_widgets[i]->IsVisible())
		{
			switch (m_widgets[i]->GetType())
			{
			case Gui::WT_BUTTON:
				FillButton(m_widgets[i], m_gui.m_vertices, m_gui.m_max_vertices, ic);
				{
					Gui::Button* btn = static_cast<Gui::Button*>(m_widgets[i]);
					m_font_builder.FillBuffer(
						btn->GetFontId(), 
						btn->GetFontSize(), 
						&btn->GetText(), 
						Vector3(-(m_width / 2) + btn->GetX() + 5 + btn->GetTextPosition().x, ((m_height / 2) - 25 - btn->GetTextPosition().y) - btn->GetY(), 0.0f), 
						0xff000000, 
						m_font.m_vertices, 
						m_font.m_renderable.m_count);

					m_render_system->UpdateTexture(&m_font.m_texture, m_font_builder.GetImage());

				}
				break;

			case Gui::WT_TEXTFIELD:
				FillTextfield(m_widgets[i], m_gui.m_vertices, m_gui.m_max_vertices, ic);
				{
					Gui::Textfield* txtfield = static_cast<Gui::Textfield*>(m_widgets[i]);
					m_font_builder.FillBuffer(
						txtfield->GetFontId(),
						txtfield->GetFontSize(),
						&txtfield->GetText(),
						Vector3(-(m_width / 2) + txtfield->GetX() + 5 + txtfield->GetTextPosition().x, ((m_height / 2) - 25 - txtfield->GetTextPosition().y) - txtfield->GetY(), 0.0f),
						0xff000000,
						m_font.m_vertices,
						m_font.m_renderable.m_count);

					m_render_system->UpdateTexture(&m_font.m_texture, m_font_builder.GetImage());
				}
				break;
			}
		}
	}

	
	m_gui.m_renderable.m_count = ic/4*6;
	m_font.m_renderable.m_count = m_font.m_renderable.m_count / 4*6;



	m_render_system->UpdateVertexBuffer(&m_gui.m_renderable.m_vertex_buffer, m_gui.m_max_vertices * sizeof(Gui::Vertex), m_gui.m_vertices);
	m_render_system->UpdateVertexBuffer(&m_font.m_renderable.m_vertex_buffer, m_font.m_max_vertices * sizeof(Text::Vertex), m_font.m_vertices); 
}

void GuiManager::Render()
{
	m_render_system->SelectShader(&m_gui.m_shader);
	m_render_system->SelectIndexBuffer(&m_gui.m_renderable.m_index_buffer);
	m_render_system->SelectVertexBuffer(&m_gui.m_renderable.m_vertex_buffer, 0);
	m_render_system->SelectRasterizerState(&m_rasterizer);
	m_render_system->SelectDepthState(&m_depth);

	m_render_system->SelectTexture(m_button_icons, 0);
	
	m_render_system->Apply();
	m_render_system->DrawIndexed(PRIM_TRIANGLE_LIST, 0, 0, m_gui.m_renderable.m_count);

	m_render_system->SelectTexture(&m_font.m_texture, 0);
	m_render_system->SelectShader(&m_font.m_shader);
	m_render_system->SelectIndexBuffer(&m_font.m_renderable.m_index_buffer);
	m_render_system->SelectVertexBuffer(&m_font.m_renderable.m_vertex_buffer, 0);
	m_render_system->Apply();
	m_render_system->DrawIndexed(PRIM_TRIANGLE_LIST, 0, 0, m_font.m_renderable.m_count);
}

void GuiManager::AttachWidget(Gui::Widget* widget)
{
	m_widgets.push_back(widget);
}

void GuiManager::DetachWidget(Gui::Widget* widget)
{
	std::vector<Gui::Widget*>::iterator it = m_widgets.begin();

	while (it != m_widgets.end())
	{
		if ((*it) == widget)
		{
			m_widgets.erase(it);
			break;
		}
		it++;
	}
}


FontId GuiManager::LoadFont(const String& filename)
{
	return m_font_builder.LoadFontFace(filename);
}

void GuiManager::OnMouseMove(IEvent* event)
{
	MouseMoveEvent* evt = static_cast<MouseMoveEvent*>(event);
}

void GuiManager::OnMouseButton(IEvent* event)
{
	MouseButtonEvent* evt = static_cast<MouseButtonEvent*>(event);
	
}

static void draw_rect(float x, float y, float z, float w, float h, float tx, float ty, float tw, float th, unsigned int color, Gui::Vertex* vertices, unsigned int size, unsigned int& pos)
{
	unsigned int i = 0;

	vertices[pos+i].color = color;
	vertices[pos+i].position.x = x;
	vertices[pos+i].position.y = y;
	vertices[pos+i].position.z = z;
	vertices[pos+i].texcoord.x = tx;
	vertices[pos+i].texcoord.y = ty+th;
	i++;

	vertices[pos+i].color = color;
	vertices[pos+i].position.x = x+w;
	vertices[pos+i].position.y = y;
	vertices[pos+i].position.z = z;
	vertices[pos+i].texcoord.x = tx+tw;
	vertices[pos+i].texcoord.y = ty+th;
	i++;

	vertices[pos+i].color = color;
	vertices[pos+i].position.x = x+w;
	vertices[pos+i].position.y = y+h;
	vertices[pos+i].position.z = z;
	vertices[pos+i].texcoord.x = tx+tw;
	vertices[pos+i].texcoord.y = ty;
	i++;

	vertices[pos+i].color = color;
	vertices[pos+i].position.x = x;
	vertices[pos+i].position.y = y+h;
	vertices[pos+i].position.z = z;
	vertices[pos+i].texcoord.x = tx;
	vertices[pos+i].texcoord.y = ty;
	i++;

	pos += i;
}

void GuiManager::FillButton(Gui::Widget* widget, Gui::Vertex* vertices, unsigned int size, unsigned int& pos)
{
	Gui::Button* button = static_cast<Gui::Button*>(widget);
	float x,y,z,w,h;
	float tx, ty, tw, th;
	w = button->GetWidth();
	h = button->GetHeight();
	x = -(m_width / 2) + button->GetX();
	y = ((m_height / 2) - h) - button->GetY();
	z = 0.001f;
	
	tx = button->GetTX();
	ty = button->GetTY();
	tw = button->GetTW();
	th = button->GetTH();
	
	draw_rect(x, y, z, w, h, tx, ty, tw, th, button->GetColor(), vertices, size, pos);
	//draw_rect(x-2, y-2, z, w+4, h+4, tx, ty, tw, th, 0xff000000, vertices, size, pos);
	
}


void GuiManager::FillTextfield(Gui::Widget* widget, Gui::Vertex* vertices, unsigned int size, unsigned int& pos)
{
	Gui::Textfield* textfield = static_cast<Gui::Textfield*>(widget);
	float x,y,z,w,h;
	float tx, ty, tw, th;

	w = textfield->GetWidth();
	h = textfield->GetHeight();
	x = -(m_width / 2) + textfield->GetX();
	y = ((m_height / 2) - h) - textfield->GetY();
	z = 0.001f;

	tx = textfield->GetTX();
	ty = textfield->GetTY();
	tw = textfield->GetTW();
	th = textfield->GetTH();

	draw_rect(x, y, z, w, h, tx, ty, tw, th, textfield->GetColor(), vertices, size, pos);
	//draw_rect(x-2, y-2, z, w+4, h+4, tx, ty, tw, th, 0xff000000, vertices, size, pos);
}

void GuiManager::ClearAllWidgets(){
	if (!m_widgets.empty()){
		//for (auto it = m_widgets.begin(); it != m_widgets.end(); it++){
		//	delete *it;
		//	*it = nullptr;
		//}
		m_widgets.clear();
	}
}